// Copyright (c) 2010, Anthony Cassidy
// All rights reserved.
// 
// Redistribution and use in source and binary forms, with or without modification, are 
// permitted provided that the following conditions are met:
// 
//     * Redistributions of source code must retain the above copyright notice, this list 
//         of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright notice, this 
//         list of conditions and the following disclaimer in the documentation and/or 
//         other materials provided with the distribution.
//     * Neither the name of the AntsAdventureGameCreator nor the names of its contributors 
//         may be used to endorse or promote products derived from this software without 
//         specific prior written permission.
// 
// 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 
// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 
// SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 
// THE POSSIBILITY OF SUCH DAMAGE.
#include "XStdafx.h"
#include "XChoices.h"
#include "XRoomObject.h"
#include "XChoicesDialog.h"

XChoices::XChoices()
:m_choicesAsAutoPtr(new Choices())// del in auto_ptr
,m_choices(m_choicesAsAutoPtr.get())
{
    m_netDownArrowClicks = 0;
    m_blackImageForEmptySlot = QImage(1, 1, QImage::Format_ARGB32_Premultiplied);
    m_blackImageForEmptySlot.fill(0);
    m_speaker = NULL;
    m_isVisible = true;
}

XChoicesSlotCollection& XChoices::Slots()
{ 
    return m_slots;
}


bool XChoices::IsDownArrowVisible()
{
    return m_slots.Count() + m_netDownArrowClicks < m_visibleItemsCache.size();
}

bool XChoices::IsUpArrowVisible()
{
    return m_netDownArrowClicks>0 && m_visibleItemsCache.size() >0;
}

void XChoices::DrawDownArrowIfVisible(QPoint mousePos, QPainter& displayPainter, QPainter& zbufferPainter, QRgb zbufferColor, bool /*forceRolloverOff*/)
{
    if(m_isVisible)
    {
        zbufferPainter.fillRect( m_rectForDownArrow, QBrush(zbufferColor));
        if(!IsDownArrowVisible())
        {
            displayPainter.drawImage(m_rectForDownArrow, m_blackImageForEmptySlot);
        }
        else if(m_rectForDownArrow.contains(mousePos))
        {
            displayPainter.drawImage(0,0,m_imageForDownArrowRollover);
        }
        else
        { 
            displayPainter.drawImage(0,0,m_imageForDownArrow);
        }
    }
}

void XChoices::DrawUpArrowIfVisible(QPoint mousePos, QPainter& displayPainter, QPainter& zbufferPainter, QRgb zbufferColor, bool forceRolloverOff)
{
    if(m_isVisible)
    {
        zbufferPainter.fillRect( m_rectForUpArrow, QBrush(zbufferColor));
        if(!IsUpArrowVisible())
        {
            displayPainter.drawImage(m_rectForUpArrow, m_blackImageForEmptySlot);
        }
        else if(m_rectForUpArrow.contains(mousePos))
        {
            displayPainter.drawImage(0,0,m_imageForUpArrowRollover);
        }

    }
    else displayPainter.drawImage(0,0,m_imageForUpArrow);
}


void XChoices::DrawSlotIfVisible(int i, QPainter& displayPainter, QPainter& zbufferPainter, QRgb zbufferColor, bool forceRolloverOff)
{
    if(m_isVisible)
    {
        m_slots.DrawSlotIfVisible(i,displayPainter, zbufferPainter, zbufferColor, forceRolloverOff);	
    }
}

bool XChoices::IsMouseOverSlot(int i, QPoint pos)
{
    return m_slots.IsMouseOverSlot(i,pos);	
}

bool XChoices::IsMouseOverUpArrow(QPoint pos)
{
    return m_rectForUpArrow.contains(pos);
}

bool XChoices::IsMouseOverDownArrow(QPoint pos)
{
    return m_rectForDownArrow.contains(pos);
}

void XChoices::TriggerUpArrow()
{
    m_netDownArrowClicks--; //relies on gui to enforce validity
    UpdateImages();
}

void XChoices::TriggerDownArrow()
{
    m_netDownArrowClicks++; //relies on gui to enforce validity
    UpdateImages();
}

void XChoices::TriggerSlot(int index)
{
    (index=index);
}

void WipeCanvas(QImage& image);


QImage  GenerateImageForChoicesArrowButton(Qt::ArrowType arrowType, QRect rect, bool isHighlight)
{
    QImage image(rect.right(), rect.bottom(), QImage::Format_ARGB32_Premultiplied);

    // set alpha channel BEFORE painter
    WipeCanvas(image);

    //initialize painter
    QPainter painter;
    bool isOk = painter.begin(&image);
    if(isOk)
    {
        //painter.fillRect(rect,Qt::black);
        int top = rect.top()+3;
        int bottom = rect.bottom()-3;
        int left = rect.left()+3;
        int right = rect.right()-3;
        int midx = (left+right)/2;
        int height = bottom - top;

        // different color
        if(isHighlight)
        {
            painter.setBrush(Qt::yellow);
        }else
        {
            painter.setBrush(Qt::blue);
        }

        // different shape
        if(arrowType == Qt::DownArrow)
        {
            QPointF points[3] = {QPoint(midx, bottom),QPoint(midx-height, top),QPoint(midx+height, top)};
            painter.drawPolygon(points,3);
        }
        else
        {
            QPointF points[3] = {QPoint(midx,top),QPoint(midx-height,bottom),QPoint(midx+height,bottom)};
            painter.drawPolygon(points,3);
        }
        bool isOk2 = painter.end();
        (isOk2 = isOk2);
        ASSERT(isOk2 && "painter end failed");
    }

    return image;
}

void XChoices::ReviseNetDownArrowClicksDownIfNecessary()
{
    if(m_netDownArrowClicks >  m_visibleItemsCache.size() - m_slots.Count())
    {
        m_netDownArrowClicks = m_visibleItemsCache.size() - m_slots.Count();
    }

    if(m_netDownArrowClicks < 0)
    {
        m_netDownArrowClicks = 0;
    }
}



void XChoices::SetRect(QRect rect)
{
    m_rect = rect;
    UpdateRects();
}

void XChoices::UpdateRects()
{
    m_slots.ResizeRectList(m_rect, m_rectForUpArrow, m_rectForDownArrow);
    UpdateImagesAndArrows();// a resize, ie redraw the arrows
}

void XChoices::UpdateItemVisibility()
{
    UpdateVisibleItemsCache();
    UpdateImages();// a re-assign
}

void XChoices::UpdateVisibleItemsCache() //only needs to be called on Visibility change callback
{
    m_visibleItemsCache.clear();
    m_visibleReturnIndicesCache.clear();
    unsigned int count = m_choices->m_indices.size();
    for(unsigned int i = 0;i<count;i++)
    {
        QString item = m_choices->m_displayStrings[i];
        int indexToReturn = m_choices->m_indices[i];
        if(m_choices->m_isVisible[i])
        {
            m_visibleItemsCache.push_back(item);
            m_visibleReturnIndicesCache.push_back(indexToReturn);
        }
    }
}


void XChoices::UpdateImagesAndArrows()
{
    // needs to be called when rect changes
    if(m_rectForDownArrow.right() != -1 && m_rectForDownArrow.bottom() != -1)
    {
        m_imageForDownArrow = GenerateImageForChoicesArrowButton(Qt::DownArrow, m_rectForDownArrow, false);
        m_imageForDownArrowRollover = GenerateImageForChoicesArrowButton(Qt::DownArrow, m_rectForDownArrow, true);
    }

    if(m_rectForUpArrow.right() != -1 && m_rectForUpArrow.bottom() != -1)
    {
        m_imageForUpArrow = GenerateImageForChoicesArrowButton(Qt::UpArrow, m_rectForUpArrow, false);
        m_imageForUpArrowRollover = GenerateImageForChoicesArrowButton(Qt::UpArrow, m_rectForUpArrow, true);
    }

    UpdateImages();
}

void XChoices::UpdateImages()
{
    ReviseNetDownArrowClicksDownIfNecessary();
    for(int i=0;i<m_slots.Count();i++)
    {
        if((i+m_netDownArrowClicks) < m_visibleItemsCache.size())
        {
            QString displayText = m_visibleItemsCache[i+m_netDownArrowClicks];
            m_slots.UpdateSlotImage(i,displayText);
        }
        else
        {

            m_slots.UpdateSlotImage(i, "");
        }
    }
}

void XChoices::SetHighlightedSlot(int index)
{
    m_slots.SetHighlightedSlot(index);
}


void XChoices::SetChoices(const Choices choices)
{
    m_choicesAsAutoPtr.reset(new Choices(choices));// del in autoptr
    m_choices = m_choicesAsAutoPtr.get();
    UpdateItemVisibility();
}
void XChoices::TriggerChoice(QPoint pos)
{
    for(int i=0;i<m_slots.Count();i++)
    {
        if(IsMouseOverSlot(i,pos))
        {
            int indexToReturn = m_visibleReturnIndicesCache[i+m_netDownArrowClicks];
            QString displayText = m_visibleItemsCache[i+m_netDownArrowClicks];
            FinishChoices(displayText, indexToReturn);
            break;
        }
    }
}

void XChoices::FinishChoices(QString displayText, int indexToReturn)
{
    SetVisible(false);
    if(m_speaker)
    {
        // it is at the end of Say that flow control teleports back to script thread.
        m_speaker->SetChoicesResult( indexToReturn);
        m_speaker->Say( displayText );
    }
}

bool XChoices::IsMouseOverChoices(QPoint pos)
{
    bool isMouseOverChoices = m_rect.contains(pos);
    return isMouseOverChoices;
}


void XChoices::SetSpeaker(XRoomObject& speaker)
{
    m_speaker = &speaker;
}

void XChoices::DoChoicesDialog(QWidget* parent)
{
    bool isUsingDialog = false;
#ifdef _DEBUG
    isUsingDialog = true;
#endif

    if(isUsingDialog)
    {
        XChoicesDialog d(m_choicesAsAutoPtr, parent);
        int i=0;
        i =  d.Exec();

        // i == 0, and i == 1 are exit codes from the dialog.
        if(i>=2)
        {
            QString displayText = m_choices->m_displayStrings[i-2];
            int indexToReturn = m_choices->m_indices[1-2];

            FinishChoices(displayText, indexToReturn);
            return;
        }
    }
}

void XChoices::SetVisible(bool isVisible)
{
    m_isVisible = isVisible;
}
